package persistence

import (
	"context"
	"fmt"
	"time"

	"baseService/internal/infrastructure/config"
	"baseService/internal/pkg/logger"

	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlserver"
	"gorm.io/gorm"
	gormLogger "gorm.io/gorm/logger"
)

// DatabaseManager 数据库管理器
type DatabaseManager struct {
	config           *config.Config
	logger           logger.Logger
	db               *gorm.DB
	migrationManager *MigrationManager
}

// NewDatabaseManager 创建数据库管理器
func NewDatabaseManager(cfg *config.Config, logger logger.Logger) *DatabaseManager {
	return &DatabaseManager{
		config: cfg,
		logger: logger,
	}
}

// Initialize 初始化数据库连接
func (dm *DatabaseManager) Initialize() error {
	dm.logger.Info("开始初始化数据库连接...")

	// 创建数据库连接
	db, err := dm.createConnection()
	if err != nil {
		return fmt.Errorf("创建数据库连接失败: %w", err)
	}

	dm.db = db
	dm.migrationManager = NewMigrationManager(db, dm.logger)

	// 配置连接池
	if err := dm.configureConnectionPool(); err != nil {
		return fmt.Errorf("配置数据库连接池失败: %w", err)
	}

	// 测试连接
	if err := dm.testConnection(); err != nil {
		return fmt.Errorf("数据库连接测试失败: %w", err)
	}

	// 预热连接池（如果启用）
	if dm.config.Database.EnableWarmup {
		if err := dm.warmupConnectionPool(); err != nil {
			dm.logger.Warnf("数据库连接池预热失败: %v", err)
			// 预热失败不影响启动，只记录警告
		}
	} else {
		dm.logger.Info("数据库连接池预热已禁用")
	}

	dm.logger.Infof("数据库连接初始化成功 - driver: %s, host: %s, port: %d, database: %s",
		dm.config.Database.Driver, dm.config.Database.Host, dm.config.Database.Port, dm.config.Database.Database)

	return nil
}

// createConnection 创建数据库连接
func (dm *DatabaseManager) createConnection() (*gorm.DB, error) {
	var dialector gorm.Dialector
	var dsn string

	switch dm.config.Database.Driver {
	case "mysql":
		dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
			dm.config.Database.Username,
			dm.config.Database.Password,
			dm.config.Database.Host,
			dm.config.Database.Port,
			dm.config.Database.Database,
			dm.config.Database.Charset,
		)
		dialector = mysql.Open(dsn)

	case "postgres":
		dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
			dm.config.Database.Host,
			dm.config.Database.Username,
			dm.config.Database.Password,
			dm.config.Database.Database,
			dm.config.Database.Port,
		)
		dialector = postgres.Open(dsn)

	case "sqlserver":
		dsn = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s",
			dm.config.Database.Username,
			dm.config.Database.Password,
			dm.config.Database.Host,
			dm.config.Database.Port,
			dm.config.Database.Database,
		)
		dialector = sqlserver.Open(dsn)

	default:
		return nil, fmt.Errorf("不支持的数据库驱动: %s", dm.config.Database.Driver)
	}

	// 配置 GORM
	gormConfig := &gorm.Config{
		Logger: dm.createGormLogger(),
		NowFunc: func() time.Time {
			return time.Now().Local()
		},
		// 禁用外键约束检查 (可选，根据需要调整)
		DisableForeignKeyConstraintWhenMigrating: false,
	}

	db, err := gorm.Open(dialector, gormConfig)
	if err != nil {
		return nil, fmt.Errorf("打开数据库连接失败: %w", err)
	}

	return db, nil
}

// createGormLogger 创建 GORM 日志器
func (dm *DatabaseManager) createGormLogger() gormLogger.Interface {
	var logLevel gormLogger.LogLevel

	// 直接使用配置的日志级别
	switch dm.config.Database.LogLevel {
	case "silent":
		logLevel = gormLogger.Silent
	case "error":
		logLevel = gormLogger.Error
	case "warn":
		logLevel = gormLogger.Warn
	case "info":
		logLevel = gormLogger.Info
	default:
		logLevel = gormLogger.Warn // 默认使用warn级别
	}

	// 使用新的 GormLogger，更好地集成 zap
	return NewGormLogger(
		dm.logger,
		gormLogger.Config{
			SlowThreshold:             200 * time.Millisecond, // 慢查询阈值
			LogLevel:                  logLevel,
			IgnoreRecordNotFoundError: true,  // 忽略记录未找到错误
			Colorful:                  false, // 禁用颜色输出
		},
	)
}

// configureConnectionPool 配置数据库连接池
func (dm *DatabaseManager) configureConnectionPool() error {
	sqlDB, err := dm.db.DB()
	if err != nil {
		return err
	}

	// 设置最大空闲连接数
	sqlDB.SetMaxIdleConns(dm.config.Database.MaxIdleConns)

	// 设置最大打开连接数
	sqlDB.SetMaxOpenConns(dm.config.Database.MaxOpenConns)

	// 设置连接最大生存时间
	sqlDB.SetConnMaxLifetime(dm.config.Database.ConnMaxLifetime)

	dm.logger.Infof("数据库连接池配置完成 - max_idle_conns: %d, max_open_conns: %d, conn_max_lifetime: %v",
		dm.config.Database.MaxIdleConns, dm.config.Database.MaxOpenConns, dm.config.Database.ConnMaxLifetime)

	return nil
}

// testConnection 测试数据库连接
func (dm *DatabaseManager) testConnection() error {
	sqlDB, err := dm.db.DB()
	if err != nil {
		return err
	}

	if err := sqlDB.Ping(); err != nil {
		return fmt.Errorf("数据库连接测试失败: %w", err)
	}

	dm.logger.Info("数据库连接测试成功")
	return nil
}

// warmupConnectionPool 预热数据库连接池
func (dm *DatabaseManager) warmupConnectionPool() error {
	sqlDB, err := dm.db.DB()
	if err != nil {
		return fmt.Errorf("获取底层数据库实例失败: %w", err)
	}

	// 获取配置的最小空闲连接数，预热到这个数量
	targetConnections := dm.config.Database.MaxIdleConns
	if targetConnections <= 0 {
		targetConnections = 5 // 默认预热5个连接
	}

	dm.logger.Infof("开始预热数据库连接池 - target_connections: %d", targetConnections)

	startTime := time.Now()
	successCount := 0

	// 使用goroutine并发预创建连接，提高预热速度
	type warmupResult struct {
		index int
		err   error
	}

	resultChan := make(chan warmupResult, targetConnections)

	for i := 0; i < targetConnections; i++ {
		go func(index int) {
			// 通过执行简单查询来预创建连接
			timeout := dm.config.Database.WarmupTimeout
			if timeout <= 0 {
				timeout = 5 * time.Second // 默认5秒超时
			}

			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()

			// 使用 GORM 执行简单查询，这会自动从连接池获取连接
			var result int
			err := dm.db.WithContext(ctx).Raw("SELECT 1").Scan(&result).Error

			resultChan <- warmupResult{index: index, err: err}
		}(i)
	}

	// 收集结果
	for i := 0; i < targetConnections; i++ {
		result := <-resultChan
		if result.err != nil {
			dm.logger.Debugf("预热连接失败 - connection_index: %d, error: %v", result.index, result.err)
		} else {
			successCount++
		}
	}

	// 检查连接池状态
	stats := sqlDB.Stats()
	elapsed := time.Since(startTime)

	dm.logger.Infof("数据库连接池预热完成 - target_connections: %d, success_count: %d, current_open: %d, current_idle: %d, elapsed: %v",
		targetConnections, successCount, stats.OpenConnections, stats.Idle, elapsed)

	// 如果成功率过低，返回警告错误
	if successCount < targetConnections/2 {
		return fmt.Errorf("连接池预热成功率过低: %d/%d", successCount, targetConnections)
	}

	return nil
}

// Migrate 执行数据库迁移
func (dm *DatabaseManager) Migrate() error {
	if dm.migrationManager == nil {
		return fmt.Errorf("数据库管理器未初始化")
	}

	return dm.migrationManager.AutoMigrate()
}

// Reset 重置数据库（删除所有表并重新创建）
func (dm *DatabaseManager) Reset() error {
	dm.logger.Warn("开始重置数据库...")

	// 删除所有表
	if err := dm.migrationManager.DropAllTables(); err != nil {
		return fmt.Errorf("删除表失败: %w", err)
	}

	// 重新创建表
	if err := dm.Migrate(); err != nil {
		return fmt.Errorf("重新创建表失败: %w", err)
	}

	dm.logger.Info("数据库重置完成")
	return nil
}

// GetDB 获取数据库实例
func (dm *DatabaseManager) GetDB() *gorm.DB {
	return dm.db
}

// Close 关闭数据库连接
func (dm *DatabaseManager) Close() error {
	if dm.db == nil {
		return nil
	}

	sqlDB, err := dm.db.DB()
	if err != nil {
		return err
	}

	if err := sqlDB.Close(); err != nil {
		dm.logger.Errorf("关闭数据库连接失败: %v", err)
		return err
	}

	dm.logger.Info("数据库连接已关闭")
	return nil
}

// GetConnectionStats 获取连接池统计信息
func (dm *DatabaseManager) GetConnectionStats() (map[string]interface{}, error) {
	if dm.db == nil {
		return nil, fmt.Errorf("数据库未初始化")
	}

	sqlDB, err := dm.db.DB()
	if err != nil {
		return nil, err
	}

	stats := sqlDB.Stats()
	return map[string]interface{}{
		"max_open_connections": stats.MaxOpenConnections,
		"open_connections":     stats.OpenConnections,
		"in_use":               stats.InUse,
		"idle":                 stats.Idle,
		"wait_count":           stats.WaitCount,
		"wait_duration":        stats.WaitDuration,
		"max_idle_closed":      stats.MaxIdleClosed,
		"max_idle_time_closed": stats.MaxIdleTimeClosed,
		"max_lifetime_closed":  stats.MaxLifetimeClosed,
	}, nil
}
